/**
 * Copyright (c) 2016. Visionet and/or its affiliates. All right reserved.
 * VISIONET PROPRIETARY/CONFIDENTIAL.
 */
package com.visionet.jumper.core.domain.search;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.visionet.jumper.core.domain.search.filter.*;
import org.springframework.data.domain.Sort.Direction;

import com.visionet.jumper.core.domain.bean.Page;
import com.visionet.jumper.core.domain.search.enumdata.SearchOperator;

public abstract class Searchable {

	private Page page;
	
	private String tableName;
	
	private String aliasName;

	private Class<?> transfer;
	
	private List<SearchFilter> searchFilters = new ArrayList<SearchFilter>();
	
	private List<SearchFilter> havingFilters = new ArrayList<SearchFilter>();
	
	private List<SearchSort> searchSorts = new ArrayList<SearchSort>();
	
	private List<String> groupByList = new ArrayList<String>();
	
	private List<String> selectList = new ArrayList<String>();
	
	private Map<String, String> aliasMap = new LinkedHashMap<String, String>();
	
	private Map<String, String> linkConditions = new LinkedHashMap<String, String>();

	private List<JoinTableCondition> joinTable = new ArrayList<>();

	protected Searchable(){}
	/**
	 * 添加关联条件
	 * @param field1 字段1
	 * @param field2 字段2
	 * @return
	 */
	protected Searchable linkConditions(String field1, String field2) {
		linkConditions.put(field1, field2);
		return this;
	}

	/**
	 * 添加查询主表
	 * @param tableName 表名
	 * @param alias 别名
	 * @return
	 */
	protected Searchable table(String tableName, String alias) {
		this.tableName = tableName;
		this.aliasName = alias;
		return this;
	}
	
	/**
	 * 添加关联表
	 * @param tableName 表名
	 * @param alias 别名
	 */
	protected Searchable join(String tableName, String alias) {
		this.getAliasMap().put(tableName, alias);
		return this;
	}
	
	/**
	 * 添加关联表
	 * @param c 类名
	 * @param alias 别名
	 */
	protected Searchable join(Class<?> c, String alias) {
		return join(c.getName(), alias);
	}
	
	/**
	 * 添加条件：等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable eq(String field, Object obj) {
		return eq(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable eq(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.eq));
		return this;
	}
	
	/**
	 * 添加条件：不等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable ne(String field, Object obj) {
		return ne(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：不等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable ne(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.ne));
		return this;
	}
	
	/**
	 * 添加条件：大于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable gt(String field, Object obj) {
		return gt(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：大于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable gt(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.gt));
		return this;
	}
	
	/**
	 * 添加条件：大于等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable gte(String field, Object obj) {
		return gte(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：大于等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable gte(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.gte));
		return this;
	}
	
	/**
	 * 添加条件：小于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable lt(String field, Object obj) {
		return lt(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：小于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable lt(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.lt));
		return this;
	}
	
	/**
	 * 添加条件：小于等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable lte(String field, Object obj) {
		return lte(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：小于等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable lte(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.lte));
		return this;
	}
	
	/**
	 * 添加条件：前缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable prefixLike(String field, Object obj) {
		return prefixLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：前缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable prefixLike(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.prefixLike));
		return this;
	}
	
	/**
	 * 添加条件：前缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable prefixNotLike(String field, Object obj) {
		return prefixNotLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：前缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable prefixNotLike(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.prefixNotLike));
		return this;
	}
	
	/**
	 * 添加条件：后缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable suffixLike(String field, Object obj) {
		return suffixLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：后缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable suffixLike(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.suffixLike));
		return this;
	}
	
	/**
	 * 添加条件：后缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable suffixNotLike(String field, Object obj) {
		return suffixNotLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：后缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable suffixNotLike(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.suffixNotLike));
		return this;
	}
	
	/**
	 * 添加条件：模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable like(String field, Object obj) {
		return like(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable like(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.like));
		return this;
	}
	
	/**
	 * 添加条件：不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable notLike(String field, Object obj) {
		return notLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable notLike(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.notLike));
		return this;
	}
	
	/**
	 * 添加条件：空
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable isNull(String field, Object obj) {
		return isNull(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：空
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable isNull(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.isNull));
		return this;
	}
	
	/**
	 * 添加条件：非空
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable isNotNull(String field, Object obj) {
		return isNotNull(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：非空
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable isNotNull(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.isNotNull));
		return this;
	}
	
	/**
	 * 添加条件：包含
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable in(String field, Object obj) {
		return in(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：包含
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable in(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.in));
		return this;
	}
	
	/**
	 * 添加条件：不包含
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable notIn(String field, Object obj) {
		return notIn(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加条件：不包含
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable notIn(String field, Object obj, String type) {
		searchFilters.add(this.addCondition(field, obj, type, SearchOperator.notIn));
		return this;
	}
	
	/**
	 * 添加自定义条件
	 * @param string Example: and (field1 like ? or field2 > ?)
	 * @param obj 参数
	 */
	protected Searchable condition(String string, List<Object> obj) {
		SearchFilter condition = new Condition(string, obj);
		searchFilters.add(condition);
		return this;
	}
	
	/**
	 * 添加or条件
	 * example： and (field1 like ? or field2 > ? ...)
	 * @param orCondition1
	 * @param orCondition2
	 * @param orConditions
	 * @return
	 */
	protected Searchable or(OrCondition orCondition1, OrCondition orCondition2, OrCondition ... orConditions) {
		OrConditionGroup condition = new OrConditionGroup();
		condition.addOrCondition(orCondition1);
		condition.addOrCondition(orCondition2);
		for (OrCondition orCondition : orConditions) {
			condition.addOrCondition(orCondition);
		}
		searchFilters.add(condition);
		return this;
	}
	
	/**
	 * 添加having条件：等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingEq(String field, Object obj) {
		return havingEq(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingEq(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.eq));
		return this;
	}
	
	/**
	 * 添加having条件：不等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingNe(String field, Object obj) {
		return havingNe(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：不等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingNe(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.ne));
		return this;
	}
	
	/**
	 * 添加having条件：大于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingGt(String field, Object obj) {
		return havingGt(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：大于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingGt(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.gt));
		return this;
	}
	
	/**
	 * 添加having条件：大于等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingGte(String field, Object obj) {
		return havingGte(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：大于等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingGte(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.gte));
		return this;
	}
	
	/**
	 * 添加having条件：小于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingLt(String field, Object obj) {
		return havingLt(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：小于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingLt(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.lt));
		return this;
	}
	
	/**
	 * 添加having条件：小于等于
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingLte(String field, Object obj) {
		return havingLte(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：小于等于
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingLte(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.lte));
		return this;
	}
	
	/**
	 * 添加having条件：前缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingPrefixLike(String field, Object obj) {
		return havingPrefixLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：前缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingPrefixLike(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.prefixLike));
		return this;
	}
	
	/**
	 * 添加having条件：前缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingPrefixNotLike(String field, Object obj) {
		return havingPrefixNotLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：前缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingPrefixNotLike(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.prefixNotLike));
		return this;
	}
	
	/**
	 * 添加having条件：后缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingSuffixLike(String field, Object obj) {
		return havingSuffixLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：后缀模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingSuffixLike(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.suffixLike));
		return this;
	}
	
	/**
	 * 添加having条件：后缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingSuffixNotLike(String field, Object obj) {
		return havingSuffixNotLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：后缀模糊不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingSuffixNotLike(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.suffixNotLike));
		return this;
	}
	
	/**
	 * 添加having条件：模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingLike(String field, Object obj) {
		return havingLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：模糊匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingLike(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.like));
		return this;
	}
	
	/**
	 * 添加having条件：不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingNotLike(String field, Object obj) {
		return havingNotLike(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：不匹配
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingNotLike(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.notLike));
		return this;
	}
	
	/**
	 * 添加having条件：空
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingIsNull(String field, Object obj) {
		return havingIsNull(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：空
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingIsNull(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.isNull));
		return this;
	}
	
	/**
	 * 添加having条件：非空
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingIsNotNull(String field, Object obj) {
		return havingIsNotNull(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：非空
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingIsNotNull(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.isNotNull));
		return this;
	}
	
	/**
	 * 添加having条件：包含
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingIn(String field, Object obj) {
		return havingIn(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：包含
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingIn(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.in));
		return this;
	}
	
	/**
	 * 添加having条件：不包含
	 * @param field 字段名
	 * @param obj 值
	 * @return
	 */
	protected Searchable havingNotIn(String field, Object obj) {
		return havingNotIn(field, obj, SearchConstant.AND);
	}
	
	/**
	 * 添加having条件：不包含
	 * @param field 字段名
	 * @param obj 值
	 * @param type and/or
	 * @return
	 */
	protected Searchable havingNotIn(String field, Object obj, String type) {
		havingFilters.add(this.addCondition(field, obj, type, SearchOperator.notIn));
		return this;
	}
	
	/**
	 * 添加自定义条件
	 * @param string Example: and (field1 like ? or field2 > ?)
	 * @param obj 参数
	 */
	protected Searchable havingCondition(String string, List<Object> obj) {
		SearchFilter condition = new Condition(string, obj);
		havingFilters.add(condition);
		return this;
	}
	
	/**
	 * 添加groupBy字段
	 * @param field 字段名
	 */
	protected Searchable groupBy(String field) {
		groupByList.add(field);
		return this;
	}
	
	/**
	 * 添加升序
	 * @param field 字段名
	 */
	protected Searchable ascSort(String field) {
		SearchSort searchSort = new SearchSort(Direction.ASC.name(), field);
		searchSorts.add(searchSort);
		return this;
	}
	
	/**
	 * 添加降序
	 * @param field 字段名
	 */
	protected Searchable descSort(String field) {
		SearchSort searchSort = new SearchSort(Direction.DESC.name(), field);
		searchSorts.add(searchSort);
		return this;
	}
	
	/**
	 * 添加select字段
	 * @param field 字段名或函数
	 */
	protected Searchable select(String field) {
		selectList.add(field);
		return this;
	}

	protected Searchable addJoinTable(JoinTableCondition joinTable) {
		this.joinTable.add(joinTable);
		return this;
	}

	protected SearchFilter addCondition(String field, Object obj, String type, SearchOperator searchOperator) {
		if (SearchConstant.OR.equals(type.toLowerCase())) {
			SearchFilter orCondition = new OrCondition(field, searchOperator, obj);
			return orCondition;
		} else {
			SearchFilter andCondition = new AndCondition(field, searchOperator, obj);
			return andCondition;
		}
	}

	protected void setPage(Page page) {
		this.page = page;
	}

	public Class<?> getTransfer() {
		return transfer;
	}

	protected void setTransfer(Class<?> transfer) {
		this.transfer = transfer;
	}

	public List<SearchFilter> getSearchFilters() {
		return searchFilters;
	}

	public Page getPage() {
		return page;
	}

	public List<SearchSort> getSearchSorts() {
		return searchSorts;
	}

	public List<String> getGroupByList() {
		return groupByList;
	}

	public List<SearchFilter> getHavingFilters() {
		return havingFilters;
	}

	public List<String> getSelectList() {
		return selectList;
	}

	public Map<String, String> getAliasMap() {
		return aliasMap;
	}

	public String getTableName() {
		return tableName;
	}

	public String getAliasName() {
		return aliasName;
	}

	public Map<String, String> getLinkConditions() {
		return linkConditions;
	}

	public List<JoinTableCondition> getJoinTable() {
		return joinTable;
	}

	protected void setJoinTable(List<JoinTableCondition> joinTable) {
		this.joinTable = joinTable;
	}
}
